package com.example.roundcornerprogressbar.view.progressbar.common;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.DirectionalLayout;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.utils.Parcel;
import ohos.utils.Sequenceable;

public abstract class AnimatedRoundCornerProgressBar extends BaseRoundCornerProgressBar {
    private HiLogLabel label = new HiLogLabel(HiLog.LOG_APP,1, "RoundCorner:AnimatedRoundCornerProgressBar");
    public static final long DEFAULT_DURATION = 500L;

    private boolean isProgressAnimating = false;
    private boolean isSecondaryProgressAnimating = false;
    private float lastProgress;
    private float lastSecondaryProgress;

    private float animationSpeedScale;
    private boolean isAnimationEnabled;

    private AnimatorValue progressAnimator;
    private AnimatorValue secondaryProgressAnimator;

    public AnimatedRoundCornerProgressBar(Context context) {
        super(context);
    }

    public AnimatedRoundCornerProgressBar(Context context, AttrSet attrSet) {
        super(context, attrSet);
    }

    public AnimatedRoundCornerProgressBar(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
    }

    @Override
    public void setupStyleable(Context context, AttrSet attrs) {
        super.setupStyleable(context, attrs);
        isAnimationEnabled = false;
        animationSpeedScale = 1f;
        lastProgress = super.getProgress();
        lastSecondaryProgress = super.getSecondaryProgress();
    }

    @Override
    public float getProgress() {
        if (!isAnimationEnabled && !isProgressAnimating) {
            return super.getProgress();
        } else {
            return lastProgress;
        }
    }

    @Override
    public void setProgress(int progress) {
        setProgress((float) progress);
    }

    @Override
    public void setProgress(float progress) {
        HiLog.info(label, "setProgress, isAnimationEnabled = "+isAnimationEnabled);
        if (progress < 0) {
            progress = 0;
        } else {
            progress = Math.min(progress, max);
        }
        clearProgressAnimation();
        this.lastProgress = progress;
        if (isAnimationEnabled) {
            startProgressAnimation(super.getProgress(), progress);
        } else {
            super.setProgress(progress);
        }
    }

    @Override
    public float getSecondaryProgress() {
        if (!isAnimationEnabled && !isSecondaryProgressAnimating) {
            return super.getSecondaryProgress();
        } else {
            return lastSecondaryProgress;
        }
    }

    @Override
    public void setSecondaryProgress(int progress) {
        setSecondaryProgress((float) progress);
    }

    @Override
    public void setSecondaryProgress(float progress) {
        if (progress < 0) {
            progress = 0;
        } else {
            progress = Math.min(progress, max);
        }
        clearSecondaryProgressAnimation();
        this.lastSecondaryProgress = progress;
        if (isAnimationEnabled) {
            startSecondaryProgressAnimation(super.getSecondaryProgress(), progress);
        } else {
            super.setSecondaryProgress(progress);
        }
    }

    public float getAnimationSpeedScale() {
        return animationSpeedScale;
    }

    public void enableAnimation() {
        this.isAnimationEnabled = true;
    }

    public void disableAnimation() {
        this.isAnimationEnabled = false;
    }

    public void setAnimationSpeedScale(float scale) {
        scale = Math.min(scale, 5);
        scale = Math.max(scale, 0.2f);
        this.animationSpeedScale = scale;
    }

    public boolean isProgressAnimating() {
        return isProgressAnimating;
    }

    public boolean isSecondaryProgressAnimating() {
        return isSecondaryProgressAnimating;
    }

    protected void onProgressChangeAnimationUpdate(DirectionalLayout layout, float current, float to) {
    }

    protected void onProgressChangeAnimationEnd(DirectionalLayout layout) {
    }

    protected void stopProgressAnimationImmediately() {
        HiLog.info(label, "stopProgressAnimationImmediately, isAnimationEnabled = "+isAnimationEnabled+", isProgressAnimating = "+isProgressAnimating);

        if (isAnimationEnabled && isProgressAnimating) {
            disableAnimation();
            if (isProgressAnimating) {
                isProgressAnimating = false;
                clearProgressAnimation();
                super.setProgress(lastProgress);
            }
            if (isSecondaryProgressAnimating) {
                clearSecondaryProgressAnimation();
                isSecondaryProgressAnimating = false;
                super.setSecondaryProgress(lastProgress);
            }
            enableAnimation();
        }
    }

    private long getAnimationDuration(float from, float to, float max, float scale) {
        float diff = Math.abs(from - to);
        return (long) (diff * DEFAULT_DURATION / max * scale);
    }

    private void startProgressAnimation(float from, float to) {
        HiLog.info(label, "startProgressAnimation");
        isProgressAnimating = true;
        if (progressAnimator != null) {
            progressAnimator.setValueUpdateListener(null);
            progressAnimator.setStateChangedListener(null);
            progressAnimator.cancel();
            progressAnimator = null;
        }
        progressAnimator = new AnimatorValue();
        progressAnimator.setLoopedCount(Animator.INFINITE);
        progressAnimator.setCurveType(Animator.CurveType.LINEAR);
        progressAnimator.setDuration(getAnimationDuration(from, to, max, animationSpeedScale));
        progressAnimator.setValueUpdateListener(progressAnimationUpdateListener);
        progressAnimator.setStateChangedListener(progressAnimationAdapterListener);
        progressAnimator.start();
    }

    private AnimatorValue.ValueUpdateListener progressAnimationUpdateListener = new AnimatorValue.ValueUpdateListener() {
        @Override
        public void onUpdate(AnimatorValue animatorValue, float v) {
            onUpdateProgressByAnimation(v* max);
        }
    };

    private AnimatorListenerAdapter progressAnimationAdapterListener = new AnimatorListenerAdapter() {
        @Override
        public void onEnd(Animator var1) {
            HiLog.info(label, "AnimatorListenerAdapter, onEnd");
            isProgressAnimating = false;
            onProgressAnimationEnd();
        }

        @Override
        public void onCancel(Animator var1) {
            HiLog.info(label, "AnimatorListenerAdapter, onCancel");
            isProgressAnimating = false;
        }
    };

    private void onUpdateProgressByAnimation(float progress) {
        super.setProgress(progress);
        onProgressChangeAnimationUpdate(layoutProgress, progress, lastProgress);
    }

    private void onProgressAnimationEnd() {
        onProgressChangeAnimationEnd(layoutProgress);
    }

    private void restoreProgressAnimationState() {
        if (isProgressAnimating) {
            startProgressAnimation(super.getProgress(), lastProgress);
        }
    }

    private void clearProgressAnimation() {
        HiLog.info(label, "clearProgressAnimation");
        if (progressAnimator != null && progressAnimator.isRunning()) {
            HiLog.info(label, "clearProgressAnimation, need cancel");
            progressAnimator.cancel();
        }
    }

    private void startSecondaryProgressAnimation(float from, float to) {
        isSecondaryProgressAnimating = true;
        if (secondaryProgressAnimator != null) {
            secondaryProgressAnimator.setValueUpdateListener(null);
            secondaryProgressAnimator.setStateChangedListener(null);
            secondaryProgressAnimator.cancel();
            secondaryProgressAnimator = null;
        }
        secondaryProgressAnimator = new AnimatorValue();
        progressAnimator.setLoopedCount(Animator.INFINITE);
        progressAnimator.setCurveType(Animator.CurveType.LINEAR);
        secondaryProgressAnimator.setDuration(getAnimationDuration(from, to, max, animationSpeedScale));
        secondaryProgressAnimator.setValueUpdateListener(secondaryProgressAnimationUpdateListener);
        secondaryProgressAnimator.setStateChangedListener(secondaryProgressAnimationAdapterListener);
        secondaryProgressAnimator.start();
    }

    private AnimatorValue.ValueUpdateListener secondaryProgressAnimationUpdateListener = new AnimatorValue.ValueUpdateListener() {
        @Override
        public void onUpdate(AnimatorValue animatorValue, float v) {
            onUpdateSecondaryProgressByAnimation(v*max);
        }
    };

    private AnimatorListenerAdapter secondaryProgressAnimationAdapterListener = new AnimatorListenerAdapter() {
        @Override
        public void onCancel(Animator var1) {
            isSecondaryProgressAnimating = false;
        }

        @Override
        public void onEnd(Animator var1) {
            isSecondaryProgressAnimating = false;
            onSecondaryProgressAnimationEnd();
        }
    };

    private void onUpdateSecondaryProgressByAnimation(float progress) {
        super.setSecondaryProgress(progress);
        onProgressChangeAnimationUpdate(layoutSecondaryProgress, progress, lastProgress);
    }

    private void onSecondaryProgressAnimationEnd() {
        onProgressChangeAnimationEnd(layoutSecondaryProgress);
    }

    private void restoreSecondaryProgressAnimationState() {
        if (isSecondaryProgressAnimating) {
            startSecondaryProgressAnimation(super.getSecondaryProgress(), lastSecondaryProgress);
        }
    }

    private void clearSecondaryProgressAnimation() {
        if (secondaryProgressAnimator != null && secondaryProgressAnimator.isRunning()) {
            secondaryProgressAnimator.cancel();
        }
    }

    protected static class SavedState implements Sequenceable {
        boolean isProgressAnimating;
        boolean isSecondaryProgressAnimating;
        float lastProgress;
        float lastSecondaryProgress;
        float animationSpeedScale;
        boolean isAnimationEnabled;

        public boolean marshalling(Parcel out) {
            return out.writeByte((byte) (this.isProgressAnimating ? 1 : 0)) &&
                    out.writeByte((byte) (this.isSecondaryProgressAnimating ? 1 : 0)) &&
                    out.writeFloat(this.lastProgress) &&
                    out.writeFloat(this.lastSecondaryProgress) &&
                    out.writeFloat(this.animationSpeedScale) &&
                    out.writeByte((byte) (this.isAnimationEnabled ? 1 : 0));
        }

        @Override
        public boolean unmarshalling(Parcel in) {
            this.isProgressAnimating = in.readByte() != 0;
            this.isSecondaryProgressAnimating = in.readByte() != 0;
            this.lastProgress = in.readFloat();
            this.lastSecondaryProgress = in.readFloat();
            this.animationSpeedScale = in.readFloat();
            this.isAnimationEnabled = in.readByte() != 0;
            return true;
        }

        public static final Sequenceable.Producer PRODUCER = new Sequenceable.Producer() {

            @Override
            public SavedState createFromParcel(Parcel in) {
                SavedState instance = new SavedState();
                instance.unmarshalling(in);
                return instance;
            }
        };
    }
}
